package com.yuwen.yuwenRPC.proxy;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.yuwen.RpcApplication;
import com.yuwen.yuwenRPC.config.RegistryConfig;
import com.yuwen.yuwenRPC.config.RpcConfig;
import com.yuwen.yuwenRPC.constant.RpcConstant;
import com.yuwen.yuwenRPC.fault.RetryStrategy;
import com.yuwen.yuwenRPC.fault.RetryStrategyFactory;
import com.yuwen.yuwenRPC.fault.tolerant.TolerantStrategy;
import com.yuwen.yuwenRPC.fault.tolerant.TolerantStrategyFactory;
import com.yuwen.yuwenRPC.loadbalancer.LoadBalancer;
import com.yuwen.yuwenRPC.loadbalancer.LoadBalancerFactory;
import com.yuwen.yuwenRPC.model.RpcRequest;
import com.yuwen.yuwenRPC.model.RpcResponse;
import com.yuwen.yuwenRPC.model.ServiceMetaInfo;
import com.yuwen.yuwenRPC.protocol.*;
import com.yuwen.yuwenRPC.registry.Registry;
import com.yuwen.yuwenRPC.registry.RegistryFactory;
import com.yuwen.yuwenRPC.serializer.JdkSerializer;
import com.yuwen.yuwenRPC.serializer.Serializer;
import com.yuwen.yuwenRPC.serializer.SerializerFactory;
import com.yuwen.yuwenRPC.serializer.SerializerKeys;
import com.yuwen.yuwenRPC.server.tcp.TCPBufferHandlerWrapper;
import com.yuwen.yuwenRPC.server.tcp.VertxTcpClient;
import com.yuwen.yuwenRPC.spi.SpiLoader;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.net.NetClient;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * 服务代理(动态代理)
 */
@Slf4j
public class ServiceProxy implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //指定序列化器
        final Serializer serializer = SerializerFactory.getSerializer(RpcApplication.getRpcConfig().getSerializer());

        String serviceName = method.getDeclaringClass().getName();
        //封装请求数据
        RpcRequest rpcRequest = RpcRequest
                .builder()
                .serviceName(serviceName) // 服务名
                .methodName(method.getName())//方法名
                .parameterTypes(new Class[] {method.getReturnType()}) //返回参数类型
                .parameters(args)//返回参数
                .build();
        List<ServiceMetaInfo> serviceMetaInfoList = getRequestServiceMetaInfoList(serviceName);

        //1.rpc配置类
        RpcConfig rpcConfig = RpcApplication.getRpcConfig();
        //负载均衡
        LoadBalancer loadBalancer = LoadBalancerFactory.getInstance(rpcConfig.getLoadBalancer());
        //将方法名，请求路径作为负载均衡参数
        Map<String,Object> requestParams = new HashMap<>();
        requestParams.put("methodName",rpcRequest.getMethodName());
        ServiceMetaInfo selectedServiceMetaInfo = loadBalancer.select(requestParams,serviceMetaInfoList);
        System.err.println(selectedServiceMetaInfo+"@@@@@@@@@@@@@@@@@@");

        /**
         * 重试机制
         */
        RetryStrategy retryStrategy = RetryStrategyFactory.getInstance(rpcConfig.getRetryStrategy());
        //tcp请求

        RpcResponse rpcResponse = null;
        try {
            rpcResponse = retryStrategy.doRetry(()->
                    VertxTcpClient.tcpRequest(serializer, rpcRequest, selectedServiceMetaInfo)
            );
        } catch (Exception e) {
            /**
             * 容错机制
             */
            TolerantStrategy tolerantStrategy = TolerantStrategyFactory.getInstance(rpcConfig.getTolerantStrategy());
            rpcResponse = tolerantStrategy.doTolerant(null,e);
        }
        return rpcResponse.getData();
    }

    /**
     * 从注册中心获取请求地址
     * @param serviceName 服务名
     * @return  请求地址
     */
    private List<ServiceMetaInfo> getRequestServiceMetaInfoList(String serviceName) {
        //1.rpc配置类
        RpcConfig rpcConfig = RpcApplication.getRpcConfig();
        //2. rpc配置类中获取到 registryKey (type)
        String registryKey = rpcConfig.getRegistryConfig().getRegistryType();
        //3.通过registry工厂 创建 registry
        Registry registry = RegistryFactory.getRegistry(registryKey);
        //4.创建服务元信息
        ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
        //  4.1设置服务名
        serviceMetaInfo.setServiceName(serviceName);
//          4.2设置服务版本号
        serviceMetaInfo.setServiceVersion(RpcConstant.DEFAULT_SERVICE_VERSION);
//        5.通过服务名 得到服务元信息         getServiceName           getServiceKey @return  serviceName+serviceVersion
        List<ServiceMetaInfo> serviceMetaInfos = registry.registryDiscovery(serviceMetaInfo.getServiceKey());

        if(CollUtil.isEmpty(serviceMetaInfos)){
            throw new RuntimeException("暂无服务地址");
        }
        //返回请求地址
        return serviceMetaInfos;
    }

    /**
     * http请求
     * @param serializer 序列化器
     * @param rpcRequest rpc请求
     * @return 响应结果
     */
    private Object httpRequest(Serializer serializer,RpcRequest rpcRequest){
        try {
            //将请求数据转化为 序列化为 rpcRequest
            byte[] bytes = serializer.serialize(rpcRequest);
            //发送请求
            /**
             * 请求地址硬编码了
             */
            ServiceMetaInfo serviceMetaInfo = getRequestServiceMetaInfoList(rpcRequest.getServiceName()).get(0);
            try(HttpResponse httpResponse = HttpRequest.post(serviceMetaInfo.getServiceAddress()).body(bytes).execute()) {
                //结果字节数组
                byte[] result =httpResponse.bodyBytes();
                //封装结果字节数组 =>  反序列化
                RpcResponse rpcResponse = serializer.deserialize(result, RpcResponse.class);
                //返回数据
                return rpcResponse.getData();
            }
        } catch (IOException e) {
            e.printStackTrace();

        }
        return null;
    }

}
